﻿using System;
using System.Collections.Generic;
using System.Web;
using System.Net;
using System.Security.Principal;
using System.IO;

namespace Noix.Web
{
    public class HttpListenerWorkerRequest : HttpWorkerRequest
    {
        private HttpListenerContext _context;
        private string _virtualDir;
        private string _physicalDir;

        public HttpListenerWorkerRequest(HttpListenerContext context, string virtualDir, string physicalDir)
        {
            if (null == context)
                throw new ArgumentNullException("context","context cannot be null");
            if (String.IsNullOrEmpty(virtualDir))
                throw new ArgumentException("virtualDir cannot be null or empty", "virtualDir");
            if (String.IsNullOrEmpty(physicalDir))
                throw new ArgumentException("physicalDir cannot be null or empty", "physicalDir");

            _context = context;
            _virtualDir = virtualDir;
            _physicalDir = physicalDir;
        }

        public override void EndOfRequest()
        {
            _context.Response.OutputStream.Close();
            _context.Response.Close();
        }

        public override void FlushResponse(bool finalFlush)
        {
            _context.Response.OutputStream.Flush();
        }

        public override string GetHttpVerbName()
        {
            return _context.Request.HttpMethod;
        }

        public override string GetHttpVersion()
        {
            return "HTTP/" + _context.Request.ProtocolVersion.Major + "." + _context.Request.ProtocolVersion.Minor;
        }

        public override string GetLocalAddress()
        {
            return _context.Request.LocalEndPoint.Address.ToString();
        }

        public override int GetLocalPort()
        {
            return _context.Request.LocalEndPoint.Port;
        }

        public override string GetQueryString()
        {
            if (_context.Request.Url.Query.Length > 0)
            {
                return _context.Request.Url.Query.Substring(1);
            }
            else
            {
                return _context.Request.Url.Query;
            }
        }

        public override string GetRawUrl()
        {
            return _context.Request.RawUrl;
        }

        public override string GetRemoteAddress()
        {
            return _context.Request.RemoteEndPoint.Address.ToString();
        }

        public override int GetRemotePort()
        {
            return _context.Request.RemoteEndPoint.Port;
        }

        public override string GetUriPath()
        {
            return _context.Request.Url.LocalPath;
        }

        public override void SendKnownResponseHeader(int index, string value)
        {
            _context.Response.Headers[HttpWorkerRequest.GetKnownResponseHeaderName(index)] = value;
        }

        public override void SendResponseFromMemory(byte[] data, int length)
        {
            _context.Response.OutputStream.Write(data, 0, length);
        }

        public override void SendStatus(int statusCode, string statusDescription)
        {
            _context.Response.StatusCode = statusCode;
            _context.Response.StatusDescription = statusDescription;
        }

        public override void SendUnknownResponseHeader(string name, string value)
        {
            _context.Response.Headers[name] = value;
        }

        public override void SendResponseFromFile(IntPtr handle, long offset, long length)
        {
        }

        public override void SendResponseFromFile(string filename, long offset, long length)
        {
            if (File.Exists(filename))
            {
                FileStream infile = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
                byte[] buf = new byte[length];
                int count = infile.Read(buf, (int)offset, (int)length);
                infile.Close();
                _context.Response.OutputStream.Write(buf, 0, count);
            }
        }

        public override void CloseConnection()
        {
        }

        public override string GetAppPath()
        {
            return _virtualDir;
        }

        public override string GetAppPathTranslated()
        {
            return _physicalDir;
        }

        public override int ReadEntityBody(byte[] buffer, int size)
        {
            return _context.Request.InputStream.Read(buffer, 0, size);
        }

        public override string GetUnknownRequestHeader(string name)
        {
            return _context.Request.Headers[name];
        }

        public override string[][] GetUnknownRequestHeaders()
        {
            string[][] unknownRequestHeaders;
            System.Collections.Specialized.NameValueCollection headers = _context.Request.Headers;
            int count = headers.Count;
            List<string[]> headerPairs = new List<string[]>(count);
            for (int i = 0; i < count; i++)
            {
                string headerName = headers.GetKey(i);
                if (GetKnownRequestHeaderIndex(headerName) == -1)
                {
                    string headerValue = headers.Get(i);
                    headerPairs.Add(new string[] { headerName, headerValue });
                }
            }
            unknownRequestHeaders = headerPairs.ToArray();
            return unknownRequestHeaders;
        }

        public override string GetKnownRequestHeader(int index)
        {
            switch (index)
            {
                case HeaderUserAgent:
                    return _context.Request.UserAgent;
                default:
                    return _context.Request.Headers[GetKnownRequestHeaderName(index)];
            }
        }

        public override string GetServerVariable(string name)
        {
            // TODO: complete the list
            switch (name)
            {
                case "HTTPS":
                    return _context.Request.IsSecureConnection ? "on" : "off";
                case "HTTP_USER_AGENT":
                    return _context.Request.Headers["UserAgent"];
                case "LOGON_USER":
                    return _context.User.Identity.Name;
                case "AUTH_TYPE":
                    return _context.User.Identity.AuthenticationType;
                default:
                    return null;
            }
        }

        public override IntPtr GetUserToken()
        {
            WindowsIdentity id = (WindowsIdentity)_context.User.Identity;
            return id.Token;
        }

        public override string GetFilePath()
        {
            return _context.Request.Url.LocalPath;
        }

        public override string GetFilePathTranslated()
        {
            string s = GetFilePath();
            s = s.Substring(_virtualDir.Length);
            s = s.Replace('/', '\\');
            return _physicalDir + s;
        }

        public override string GetPathInfo()
        {
            string s1 = GetFilePath();
            string s2 = _context.Request.Url.LocalPath;
            if (s1.Length == s2.Length)
            {
                return "";
            }
            else
            {
                return s2.Substring(s1.Length);
            }
        }

        // TODO :
        //public override string GetServerName()
        //{
        //    return base.GetServerName();
        //}
    }
}
